﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace ConsoleApplication1
{

    // Grafico horizontal tiempo y vertical numero de planchas antes de ser retiradas (a)

    class Program
    {
        //Se inicializan las rutas, de acuerdo a cada PC. Se cambia sólo la ruta en el string
        static string archivo1 = "C:\\Users\\Luci\\Desktop\\Proyecto Parte C\\Test.txt";
        static string archivo2 = "C:\\Users\\Luci\\Desktop\\Proyecto Parte C\\Test_CSV.csv";
        static StreamWriter swLegible = new StreamWriter(archivo1);
        static StreamWriter swCSV = new StreamWriter(archivo2);

        static int numeroDeSimulaciones = 5; // Define cuantas simulaciones queremos realizar

        static double tiempo = 0; // Tiempo expresado en minutos
        static int tiempoMaximo = 0; // Numero de minutos que durara la simulación
        static bool lleno = false; // Boolean que nos indica si hemos llegado a la capacidad maxima
        static bool ultimoFueLleno = false; // Asi sabemos si debemos renovar el proceso
        static double numRetiro = 0; //Numero de retiro

        static int D = 0; // Parametro necesario para calcular el numero de retiros

        static double alphaExp = 0.0; // Alpha de la exponencial que representa la llegada de planchas

        static int aRetiro = 0; // Representa al valor a de la Uniforme(a,b) de los retiros
        static int bRetiro = 0; // Representa al valor b de la Uniforme(a,b) de los retiros

        static int capacidadMaxima = 0; // B planchas, capacidad maxima de la bodega

        static Random rand = new Random();

        static int numeroPlanchas = 0;
        static int numPlanchasDespues = 0;

        static void Main(string[] args)
        {
            //Ejercicio1();
            //Ejercicio2();// está con los parametros de la b.3)
            Ejercicio3();
            
        }

        /// <summary>
        /// Permite escribir en un archivo (definido en las variables globales) la información de los eventos
        /// </summary>
        private static void EscribirEnArchivo()
        {
            // Lo escribe de forma legible
            swLegible.WriteLine("Tiempo: " + tiempo + " / Numero: " + numeroPlanchas);

            // Lo escribe en Comma Sepparated Value
            swCSV.WriteLine(tiempo + ";" + numeroPlanchas);

        }

        /// <summary>
        /// Escribe la información de los eventos para el método Ejercicio3
        /// </summary>
        private static void EscribirEnArchivoC()
        {
            // Lo escribe de forma legible          
            //swLegible.WriteLine("Retiro N°: " + numRetiro + " / Numero Planchas: " + numPlanchasDespues);

            // Lo escribe en Comma Sepparated Value            
            swCSV.WriteLine(numRetiro + ";" + numPlanchasDespues) ;
        }

        /// <summary>
        /// En base a un valor D dado, calcula el numero de retiros en función de la probabilidad dada
        /// </summary>
        /// <param name="D">Parametro D dado en el enunciado</param>
        /// <returns>Retorna el numero de retiros</returns>
        public static int calcularNumeroDeRetiros(int D)
        {

            double probabilidad = rand.NextDouble();

            if (probabilidad <= 0.1)
                return (D - 10);

            else if (probabilidad <= 0.25)
                return (D - 5);

            else if (probabilidad <= 0.55)
                return (D);

            else if (probabilidad <= 0.75)
                return (D + 5);

            else if (probabilidad <= 0.95)
                return (D + 10);

            else if (probabilidad <= 1)
                return (D + 15);

            else
                return -100000;

        }

        /// <summary>
        /// Metodo para realizar la parte B del enunciado, está con los parametros de la parte b.3)
        /// </summary>
        static void Ejercicio2() 
        {

            tiempoMaximo = 50 * 60;
            numeroDeSimulaciones = 100000;
            double tiempoProximoRetiro = 0.0;
            double tiempoProximaLlegada = 0.0;

            int i = 1; // Contado de simulaciones

            string ruta = "C:\\Users\\Luci\\Desktop\\Proyecto Parte C\\Ejercicio2 - Datos Histograma_CSV.csv";//Inicializar ruta de acuerdo a la configuración de cada PC
            swCSV = new StreamWriter(ruta);
            swCSV.WriteLine("Numero de planchas al tiempo 50; ;Numero de veces que llega a limite");

            while (i <= numeroDeSimulaciones)
            {
                ruta = "C:\\Users\\Luci\\Desktop\\Proyecto Parte C\\Ejercicio2 - Datos Histograma.txt";
                swLegible = new StreamWriter(ruta);

                // Reiniciamos e inicializamos las variables
                tiempo = 0.0;
                lleno = false;
                aRetiro = 11;
                bRetiro = 25;
                D = 90;
                capacidadMaxima = 180;
                numeroPlanchas = 20;
                alphaExp = 5;


                int contadorCapMaxima = 0;

                // Calculo iniciales de los eventos:

                // Calculo de U(a,b) = a + (b-a)*U(0,1)  --> Esto es para el tiempo entre retiros

                double U = rand.NextDouble();
                tiempoProximoRetiro = aRetiro + (bRetiro - aRetiro) * U;

                // exp(lb) = -ln(u)/lb  --> Calculo del tiempo entre llegadas: 

                U = rand.NextDouble();
                tiempoProximaLlegada = (-Math.Log(U)) / alphaExp;

                while (tiempo < tiempoMaximo)
                {
                    // EscribirEnArchivo();

                    if (numeroPlanchas == capacidadMaxima) // Seteamos el boolean lleno, para poder parar la llegada de planchas en caso de ser necesario
                    {
                        lleno = true;
                        contadorCapMaxima++;
                    }
                    else
                        lleno = false;

                    if (!lleno)  // Se suspende envío cuando se ha llegado a la capacidad maxima
                    {
                        if (ultimoFueLleno)
                        {
                            // Recalculamos
                            double U2 = rand.NextDouble();  // Distribución uniforme(0,1)
                            tiempoProximaLlegada = tiempo + ((-Math.Log(U2)) / alphaExp);
                            
                        }

                        else
                        {
                            if (tiempoProximaLlegada <= tiempoProximoRetiro)        // Si es que primero hay una llegada nos dirigimos a ese evento
                            {
                                if (tiempoProximaLlegada > tiempoMaximo)
                                    break;

                                tiempo = tiempoProximaLlegada;            // Al ser un simulación basada en eventos, nos dirijimos directamente tiempo del evento
                                numeroPlanchas++;                         // Llego una plancha

                                // if (numeroPlanchas > capacidadMaxima)    
                                //    numeroPlanchas = capacidadMaxima;

                                double U2 = rand.NextDouble();  // Distribución uniforme(0,1)
                                double funcion = ((-Math.Log(U2)) / alphaExp);
                                tiempoProximaLlegada = tiempo + funcion;

                                continue;
                            }
                        }

                        ultimoFueLleno = false;
                    }
                    else
                        ultimoFueLleno = true;

                    if ((tiempoProximoRetiro <= tiempoProximaLlegada) || lleno)       // Si es que primero hay un retiro, o la bodega está llena vamos al evento de retiro (lo último debido a que suspendimos las llegadas)
                    {
                        if (tiempoProximoRetiro > tiempoMaximo)
                            break;

                        tiempo = tiempoProximoRetiro;                      // Al ser un simulación basada en eventos, nos dirijimos directamente tiempo del evento
                        int numeroDeRetiros = calcularNumeroDeRetiros(D);  // Calculamos el numero de retiros
                        numeroPlanchas = numeroPlanchas - numeroDeRetiros; // Descontamos el número de planchas correspondiente

                        if (numeroPlanchas < 0)            // El número de planchas no puede ser menor a cero y renovamos el tiempo entre llegadas
                        {
                            numeroPlanchas = 0;
                        }
                        // Calculamos el proximo retiro (la variable tiempo va sumada porque calculamos el proximo retiro desde ahora)
                        double U1 = rand.NextDouble();   // Distribución uniforme(0,1)
                        tiempoProximoRetiro = tiempo + aRetiro + (bRetiro - aRetiro) * U1;
                    }





                }

                swCSV.WriteLine(numeroPlanchas+"; ;"+contadorCapMaxima);

                swLegible.Close();
                

                i++;



            }

            swCSV.Close();
        
        
        
        
        
        }

        /// <summary>
        /// Metodo de la parte a) del enunciado que crea los correspondientes 5 archivos con los datos
        /// </summary>
        static void Ejercicio1() 

        {

            tiempoMaximo = 40 * 60;
            numeroDeSimulaciones = 5;
            double tiempoProximoRetiro = 0.0;
            double tiempoProximaLlegada = 0.0;

            int i = 1; // Contado de simulaciones

            while (i <= numeroDeSimulaciones)
            {
                string ruta = "C:\\Users\\Luci\\Desktop\\Proyecto Parte C\\Test_";
                swLegible = new StreamWriter(ruta + i + ".txt");
                swCSV = new StreamWriter(ruta + "CSV_" + i + ".csv");

                // Reiniciamos e inicializamos las variables
                tiempo = 0.0;
                lleno = false;
                aRetiro = 16;
                bRetiro = 20;
                D = 90;
                capacidadMaxima = 200;
                numeroPlanchas = 50;
                alphaExp = 5;
                swCSV.WriteLine("Tiempo,Numero de planchas");


                // Calculo iniciales de los eventos:

                // Calculo de U(a,b) = a + (b-a)*U(0,1)  --> Esto es para el tiempo entre retiros

                double U = rand.NextDouble();
                tiempoProximoRetiro = aRetiro + (bRetiro - aRetiro) * U;

                // exp(lb) = -ln(u)/lb  --> Calculo del tiempo entre llegadas: 

                U = rand.NextDouble();
                tiempoProximaLlegada = (-Math.Log(U)) / alphaExp;

                while (tiempo < tiempoMaximo)
                {
                    EscribirEnArchivo();

                    if (numeroPlanchas == capacidadMaxima) // Seteamos el boolean lleno, para poder parar la llegada de planchas en caso de ser necesario
                    {
                        lleno = true;
                    }
                    else
                        lleno = false;

                    if (!lleno)  // Se suspende envío cuando se ha llegado a la capacidad maxima
                    {
                        if (ultimoFueLleno)
                        {
                            // Recalculamos
                            double U2 = rand.NextDouble();  // Distribución uniforme(0,1)
                            tiempoProximaLlegada = tiempo + ((-Math.Log(U2)) / alphaExp);
                        }

                        else
                        {
                            if (tiempoProximaLlegada <= tiempoProximoRetiro)        // Si es que primero hay una llegada nos dirigimos a ese evento
                            {
                                if (tiempoProximaLlegada > tiempoMaximo)
                                    break;

                                tiempo = tiempoProximaLlegada;            // Al ser un simulación basada en eventos, nos dirijimos directamente tiempo del evento
                                numeroPlanchas++;                         // Llego una plancha

                                // if (numeroPlanchas > capacidadMaxima)    
                                //    numeroPlanchas = capacidadMaxima;

                                double U2 = rand.NextDouble();  // Distribución uniforme(0,1)
                                tiempoProximaLlegada = tiempo + ((-Math.Log(U2)) / alphaExp);
                                continue;
                            }
                        }

                        ultimoFueLleno = false;
                    }
                    else
                        ultimoFueLleno = true;

                    if ((tiempoProximoRetiro <= tiempoProximaLlegada) || lleno)       // Si es que primero hay un retiro, o la bodega está llena vamos al evento de retiro (lo último debido a que suspendimos las llegadas)
                    {
                        if (tiempoProximoRetiro > tiempoMaximo)
                            break;

                        tiempo = tiempoProximoRetiro;                      // Al ser un simulación basada en eventos, nos dirijimos directamente tiempo del evento
                        int numeroDeRetiros = calcularNumeroDeRetiros(D);  // Calculamos el numero de retiros
                        numeroPlanchas = numeroPlanchas - numeroDeRetiros; // Descontamos el número de planchas correspondiente

                        if (numeroPlanchas < 0)            // El número de planchas no puede ser menor a cero y renovamos el tiempo entre llegadas
                        {
                            numeroPlanchas = 0;
                        }
                        // Calculamos el proximo retiro (la variable tiempo va sumada porque calculamos el proximo retiro desde ahora)
                        double U1 = rand.NextDouble();   // Distribución uniforme(0,1)
                        tiempoProximoRetiro = tiempo + aRetiro + (bRetiro - aRetiro) * U1;
                    }





                }

                

                swLegible.Close();
                swCSV.Close();

                i++;


            }
        
        

        }


        /// <summary>
        /// Método para la parte c) del enunciado
        /// </summary>
        static void Ejercicio3()
        {           
            tiempoMaximo = 40 * 60;
            numeroDeSimulaciones = 1000;
            double tiempoProximoRetiro = 0.0;
            double tiempoProximaLlegada = 0.0;

            int i = 1; // Contador de simulaciones

            //Para crear archivos destinados a las preguntas c2 y c3, se activan cuando es necesario
            string ruta = "C:\\Users\\Luci\\Desktop\\Proyecto Parte C\\";
            swCSV = new StreamWriter(ruta + "TestCSV.csv");
            swCSV.WriteLine("Retiro, Numero de planchas");

            while (i <= numeroDeSimulaciones)
            {
                //Crea archivos necesarios para la parte a), se pueden activar si es necesario
                //string ruta = "C:\\Users\\Luci\\Desktop\\Proyecto Parte C\\Test_";
                //swLegible = new StreamWriter(ruta + i + ".txt");
                //swCSV = new StreamWriter(ruta + "CSV_" + i + ".csv");

                // Reiniciamos e inicializamos las variables
                tiempo = 0.0;
                lleno = false;
                aRetiro = 16;
                bRetiro = 20;
                D = 90;
                capacidadMaxima = 200;
                numeroPlanchas = 50;
                numRetiro = 0;//Condición necesaria para las partes c.1) y c.3), se debe obviar para la parte c.2)
                numPlanchasDespues = 0;
                alphaExp = 5;
                //Requeridas para entregar resultados de numero de planchas al instante y despues de cada retiro
                //swCSV.WriteLine("Retiro, Numero de planchas");
                //swCSV.WriteLine("Tiempo,Numero de planchas");


                // Calculo iniciales de los eventos:

                // Calculo de U(a,b) = a + (b-a)*U(0,1)  --> Esto es para el tiempo entre retiros

                double U = rand.NextDouble();
                tiempoProximoRetiro = aRetiro + (bRetiro - aRetiro) * U;

                // exp(lb) = -ln(u)/lb  --> Calculo del tiempo entre llegadas: 

                U = rand.NextDouble();
                tiempoProximaLlegada = (-Math.Log(U)) / alphaExp;

                while (tiempo < tiempoMaximo)//Restricción de tiempo para que no se exceda del tiempo de simulación
                {
                    //EscribirEnArchivo();

                    if (numeroPlanchas == capacidadMaxima) // Seteamos el boolean lleno, para poder parar la llegada de planchas en caso de ser necesario
                    {
                        lleno = true;
                    }
                    else
                        lleno = false;

                    if (!lleno)  // Se suspende envío cuando se ha llegado a la capacidad maxima
                    {
                        if (ultimoFueLleno)
                        {
                            // Recalculamos
                            double U2 = rand.NextDouble();  // Distribución uniforme(0,1)
                            tiempoProximaLlegada = tiempo + ((-Math.Log(U2)) / alphaExp);
                        }

                        else
                        {
                            if (tiempoProximaLlegada <= tiempoProximoRetiro)        // Si es que primero hay una llegada nos dirigimos a ese evento
                            {
                                if (tiempoProximaLlegada > tiempoMaximo)
                                    break;

                                tiempo = tiempoProximaLlegada;            // Al ser un simulación basada en eventos, nos dirijimos directamente tiempo del evento
                                numeroPlanchas++;                         // Llego una plancha

                                // if (numeroPlanchas > capacidadMaxima)    
                                //    numeroPlanchas = capacidadMaxima;

                                double U2 = rand.NextDouble();  // Distribución uniforme(0,1)
                                tiempoProximaLlegada = tiempo + ((-Math.Log(U2)) / alphaExp);
                                continue;
                            }
                        }

                        ultimoFueLleno = false;
                    }
                    else
                        ultimoFueLleno = true;

                    if ((tiempoProximoRetiro <= tiempoProximaLlegada) || lleno)       // Si es que primero hay un retiro, o la bodega está llena vamos al evento de retiro (lo último debido a que suspendimos las llegadas)
                    {
                        if (tiempoProximoRetiro > tiempoMaximo)
                            break;

                        tiempo = tiempoProximoRetiro;                      // Al ser un simulación basada en eventos, nos dirijimos directamente tiempo del evento
                        int numeroDeRetiros = calcularNumeroDeRetiros(D);  // Calculamos el numero de retiros
                        numeroPlanchas = numeroPlanchas - numeroDeRetiros; // Descontamos el número de planchas correspondiente

                        if (numeroPlanchas < 0)            // El número de planchas no puede ser menor a cero y renovamos el tiempo entre llegadas
                        {
                            numeroPlanchas = 0;
                        }
                        numPlanchasDespues = numeroPlanchas;
                        numRetiro++;

                        //Se mantiene aquí para escribir al mismo tiempo los resultados requeridos en a) y en c.1)
                        //EscribirEnArchivoC();

                        //Condición para registrar aquellos retiros que dejan menos de 18 planchas, se activa cuando es necesario
                        //if (numPlanchasDespues < 18)
                        //{ EscribirEnArchivoC(); }
                                               

                        //Condición para registrar el número de planchas después del retiro 50, se activa cuando es necesario
                        if (numRetiro == 50)
                        { EscribirEnArchivoC(); }

                        // Calculamos el proximo retiro (la variable tiempo va sumada porque calculamos el proximo retiro desde ahora)
                        double U1 = rand.NextDouble();   // Distribución uniforme(0,1)
                        tiempoProximoRetiro = tiempo + aRetiro + (bRetiro - aRetiro) * U1;
                    }

                    
                }                              

                i++;

                //Este código se mantiene aquí para calcular las dos variables requeridas en a) y en c.1) al mismo tiempo
                //swLegible.Close();
                //swCSV.Close();
                
            }

            //Permite saber cuantos retiros se hicieron, para obtener los valores de las probabilidades en c2)
            //swCSV.WriteLine(numRetiro);

            //Este código debe ir aquí o en el ciclo anterior, dependiendo de lo que se quiera calcular. Para la parte c2 y c3, debe mantenerse en este ciclo.
            //swLegible.Close();
            //swCSV.Close();
        }
    }
}
